/** * Copyright (c) 2002-2005, Simone Bordet * All rights reserved. * * This software is distributable under the BSD license. * See the terms of the BSD license in the documentation provided with this software. */ package foxtrot.examples; import java.awt.Container; import java.awt.GridBagLayout; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JButton; import foxtrot.Task; import foxtrot.Worker; /** * An example of how to create a Task that is interruptible. <p> * This is not provided by the Foxtrot API, because it is too much application dependent: what are * the correct actions to take when a Task is interrupted ? <br> * This implies that the Task must be collaborative, and check once in a while if it is interrupted * by another thread. * * @version $Revision: 1.3 $ */ public class InterruptExample extends JFrame { private JButton button; private boolean running; private boolean taskInterrupted; public static void main(String[] args) { InterruptExample example = new InterruptExample(); example.setVisible(true); } public InterruptExample() { super("Foxtrot Example"); final String label = "Run Task !"; button = new JButton(label); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { onButtonClick(label); } }); setDefaultCloseOperation(EXIT_ON_CLOSE); Container c = getContentPane(); c.setLayout(new GridBagLayout()); c.add(button); setSize(300, 200); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); Dimension size = getSize(); int x = (screen.width - size.width) >> 1; int y = (screen.height - size.height) >> 1; setLocation(x, y); } private void onButtonClick(final String label) { if (!running) { running = true; // We will execute a long operation, change the text signaling // that the user can interrupt the operation button.setText("Interrupt"); try { // getData() will block until the heavy operation is finished ArrayList list = getData(); // getData() finished or was interrupted ? // If was interrupted we get back a null list if (list == null) { // Task was interrupted, return quietly, we already cleaned up return; } else { // Task completed successfully, do whatever useful with the list // For example, populate a JComboBox javax.swing.DefaultComboBoxModel model = new javax.swing.DefaultComboBoxModel(list.toArray()); // The reader will finish this part :) } } catch (Exception x) { // Problems during getData(), log this exception and return x.printStackTrace(); } finally { // Restore anyway the button's text button.setText(label); // Restore anyway the interrupt status for another call setTaskInterrupted(false); // We're not running anymore running = false; } } else { // Here if we want to interrupt the Task // Restore the button text to the previous value button.setText(label); // Interrupt the task setTaskInterrupted(true); } } private ArrayList getData() throws Exception { return (ArrayList)Worker.post(new Task() { public Object run() throws Exception { System.out.println("Task started..."); ArrayList list = new ArrayList(); // A repetitive operation that checks if it is interrupted. // The heavy task must collaborate ! for (int i = 0; i < 100; ++i) { System.out.println("Heavy Operation number " + (i + 1)); // Simulate a heavy operation to retrieve data Thread.sleep(250); // Populate the data structure Object data = new Object(); list.add(data); System.out.println("Checking if task is interrupted..."); if (isTaskInterrupted()) { System.out.println("Task interrupted !"); break; } System.out.println("Task not interrupted, going on"); } if (isTaskInterrupted()) { // Task is interrupted, clean the half-populated data structure // and return from the Task list.clear(); return null; } return list; } }); } private synchronized boolean isTaskInterrupted() { // Called from the Foxtrot Worker Thread. // Must be synchronized, since the variable taskInterrupted is accessed from 2 threads. // While it is easier just to change the variable value without synchronizing, it is possible // that the Foxtrot worker thread doesn't see the change (it may cache the value of the variable // in a registry). return taskInterrupted; } private synchronized void setTaskInterrupted(boolean value) { // Called from the AWT Event Dispatch Thread. // See comments above on why it must be synchronized. taskInterrupted = value; } }